Skip to content

KAFKA-10500: Add thread option#9615

Merged
mjsax merged 20 commits into
apache:trunkfrom
wcarlson5:wcarlson-add-thread
Dec 4, 2020
Merged

KAFKA-10500: Add thread option#9615
mjsax merged 20 commits into
apache:trunkfrom
wcarlson5:wcarlson-add-thread

Conversation

@wcarlson5

Copy link
Copy Markdown
Contributor

Can add stream threads now

replace #9581 after rebase

Committer Checklist (excluded from commit message)

  • Verify design and implementation
  • Verify test coverage and CI build status
  • Verify documentation (including upgrade notes)

@wcarlson5 wcarlson5 mentioned this pull request Nov 18, 2020
3 tasks
@wcarlson5
wcarlson5 marked this pull request as ready for review November 19, 2020 16:48
@wcarlson5

Copy link
Copy Markdown
Contributor Author

@cadonna @mjsax @vvcephei Part 3 of KIP-663 add threads is ready for review

@cadonna cadonna left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR, @wcarlson5 !

Here is my feedback

Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/test/java/org/apache/kafka/streams/KafkaStreamsTest.java Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to synchronize the whole method on stateLock?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well we don't want it changing state while adding a thread

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be sufficent to check in the beginning if the Streams client is in RUNNING or REBALANCING, then optimistically create the stream thread, and before we start the stream thread check if the client is not in PENDING_SHUTDOWN and not in ERROR and not in NOT_RUNNING? State changes between RUNNING and REBALANCING should not affect adding a stream thread, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay we can check then only synchronize around the start of the thread to make sure it doesn't shutdown between the check and the starting

Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/test/java/org/apache/kafka/streams/KafkaStreamsTest.java Outdated
Comment thread streams/src/test/java/org/apache/kafka/streams/KafkaStreamsTest.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be sufficent to check in the beginning if the Streams client is in RUNNING or REBALANCING, then optimistically create the stream thread, and before we start the stream thread check if the client is not in PENDING_SHUTDOWN and not in ERROR and not in NOT_RUNNING? State changes between RUNNING and REBALANCING should not affect adding a stream thread, right?

@cadonna cadonna left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wcarlson5 Thank you for the updates!

I have just some minor comments.

Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated

@cadonna cadonna left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

I have just a nit-comment.

Call for committer review and merge: @mjsax @vvcephei @ableegoldman @guozhangwang @abbccdda

@mjsax mjsax added the streams label Dec 1, 2020

@mjsax mjsax left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of comments/questions.

Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/state/internals/ThreadCache.java Outdated
Comment thread streams/src/test/java/org/apache/kafka/streams/KafkaStreamsTest.java Outdated
Comment thread streams/src/test/java/org/apache/kafka/streams/KafkaStreamsTest.java Outdated

@wcarlson5 wcarlson5 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjsax I think I got all your comments

Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/state/internals/ThreadCache.java Outdated
Comment thread streams/src/test/java/org/apache/kafka/streams/KafkaStreamsTest.java Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i + 1 -> i

Wondering why this does not result in a test failure? (Or does it; Jenkins is still running.) -- Maybe we want to add a small test that verifies that we name threads correctly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. I don't think we make sure the thread index starts at 1. But let me fix that

Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/state/internals/ThreadCache.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated

@wcarlson5 wcarlson5 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjsax god catch on the +1. Hopefully I cleared up the remaining concerns, I think I took care of what you meant by "We should ensure that we do proper cleanup for all cases." for threads

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. I don't think we make sure the thread index starts at 1. But let me fix that

Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/state/internals/ThreadCache.java Outdated
Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also shutdown the thread if it doesn't get started, otherwise me may leak (consumer or producer) clients. But I'm actually not sure why we don't just do everything (resize cache, create thread) inside the synchronized block? I'm guessing it would deadlock due to locking on the statelock but can't we just synchronize on something else that wouldn't interfere with the StreamThread creation?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point about the shutdown of the stream thread!

Actually, I did not want to have everything in the synchronized block because I thought blocking the client state more than needed was not a good idea. I thought decreasing the size of the cache might be costly if the evicted records are forwarded downstream.
Now that you mention to synchronize on a separate lock, I noticed that we probably need to put resize, start, and cleanup in the same synchronized block. The reason is that if two threads call addStreamThread() one after the other and the later thread passes

final long cacheSizePerThread = getCacheSizePerThread(threads.size() + 1);

before the earlier thread adds the new stream thread to threads in createStreamThread(), the later thread would compute the wrong cache size.

So, I am in favor of having a separate lock that just synchronizes the threads calling addStreamThread(). Maybe we can simply synchronize the whole method (which means to synchronize with start() and close()).

Still a minor issue seems to be the synchronization betweenisRunningOrRebalancing() and streamThread.start(). If between these two calls the Streams client transits to ERROR (the global stream thread died) an IllegalStateException would be thrown from the StreamStateListener because the Streams client would try to transit from ERROR to REBALANCING. But I guess that would also happen if the Streams client transits to ERROR before the new stream thread transits to PARTITION_ASSIGNED and calls the StreamStateListener that would transit the Streams client to REBALANCING. So it needs to be fixed somewhere else.

Did I miss something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I don't think we can shutdown a thread until we have started it.

I don't think there should be a dead lock by just using the state lock around most of the method. It as mostly about cost. However I think that its is probably the safest way as it solves our problem about needing to remove a thread we have just created and we won't potentially waste time resizing the cache as such.

If we synchronize on a new lock we end up with all the problems we were trying to solve earlier anyways with the possible state changes and having to clean up un-started threads

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I don't think we can shutdown a thread until we have started it.

Have a look at

public void shutdown() {
log.info("Informed to shut down");
final State oldState = setState(State.PENDING_SHUTDOWN);
if (oldState == State.CREATED) {
// The thread may not have been started. Take responsibility for shutting down
completeShutdown(true);
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh okay, when I shutdown unstarted threads in a test I got a java.lang.IllegalStateException Unexpected state transition. But it looks like that is the client.

I added a new lock for the add threads, and shutdown the thread. I think this address the problem you found with concurrent resizes. As well as @ableegoldman 's concerns

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: If it happens that you need to push another commit, could you fix the indentation here? Sorry that I haven't noticed this before.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah good catch. the diff makes that hard to see as it was actually moved to a new method.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to bother you again with the synchronization on the stateLock, but could you explain why we still need it after we synchronize on newThread?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well newThread only syncs the addThread method. There is still the race condition between the second check of is running and starting the thread. It seems like a bad idea to leave that open as it could cause thread state changes when there shouldn't be. Starting the thread is relatively low cost so this shouldn't have much impact perf wise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expanding on this, the problem in the shutdown thread. When the join only waits for alive threads, and to be alive the thread needs to be started.

So if in between the check and the start thread another thread transitions the state to NOT_RUNNING the thread will not join in the shutdown thread. Then when it continues it will start as it passed the check and we will have a thread running after the client is shutdown.

This would be extremely though race condition to find or reproduce so best to just avoid it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't that be int i = 1; i <= threads.size(); i++? Otherwise, we would look up *-StreamThread-0" and we would not look up "*-StreamThread-" + threads.size().

Could you add some tests that check the correct naming as @mjsax suggested?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I missed that suggestion

@wcarlson5
wcarlson5 requested a review from mjsax December 3, 2020 21:06

@mjsax mjsax left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one final question...

Comment thread streams/src/main/java/org/apache/kafka/streams/KafkaStreams.java
@mjsax
mjsax merged commit 9ece7fe into apache:trunk Dec 4, 2020
@wcarlson5
wcarlson5 deleted the wcarlson-add-thread branch December 11, 2020 17:07
@mjsax mjsax added the kip Requires or implements a KIP label Jan 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kip Requires or implements a KIP streams

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants